/**
 *
 * @file rules
 * @author fanxueliang@qq.com
 */

import type {RulesItem, RulesKey} from '../interface/spec';
import {trim, startsWith, isArray, includes, isUndefined, map, filter} from 'lodash-es';
import {
    stringFormat,
    isAllTypeEmpty,
    getByteLength,
    validateUrl,
    getTextByteLength,
    getText,
    getLength,
    validateAppScheme
} from '../util/helper';
import dayjs from 'dayjs';
import type {ValidateResult, RulesFunType} from './interface';

interface TagName {
    a: string;
    em: string;
    br: string;
}

// apk下载链接
const apkDownloadUrlReg = /^(https?:\/\/)[^?#]+\/[^?#]+\.apk([?#][^?#]*)?$/i;
const imgUrlReg = /\.(png|jpg|jpeg|gif)$/i;
const flvUrlReg = /\.(flv)$/i;
const flashUrlReg = /\.(swf|flv)$/i;
const videoUrlReg = /\.(mp4|ts)$/i;
const audioUrlReg = /\.(mp3|wav|ogg|aac|wma|flac|aif|ra|ape)$/i;

const errorTextMap = {
    ERROR_EMPTY: '不能为空',
    ERROR_REGEX: '格式错误',
    ERROR_NO_PATTERN: '校验规则不能为空',
    ERROR_ENUM: '必须在可选项中选择',
    ERROR_INT: '格式不正确，请填写整数',
    ERROR_NUMBER: '格式不正确，请填写数字',
    ERROR_MIN: '不能小于{0}',
    ERROR_MAX: '不能大于{0}',
    ERROR_GT: '必须大于{0}',
    ERROR_LT: '必须小于{0}',
    ERROR_DATE: '不存在的日期',
    ERROR_LT_DATE: '必须早于{0}',
    ERROR_GT_DATE: '必须晚于{0}',
    ERROR_FIX_LENGTH: '长度必须等于{0}字符',
    ERROR_MIN_LENGTH: '长度不能小于{0}字符',
    ERROR_MAX_LENGTH: '长度不能大于{0}字符',
    ERROR_COUNT: '个数必须等于{0}',
    ERROR_MIN_COUNT: '个数不能少于{0}',
    ERROR_MAX_COUNT: '个数不能多于{0}',
    ERROR_MULTIPLE_COUNT: '个数必须是{0}的倍数',
    ERROR_FIX_BYTE_LENGTH: '长度必须等于{0}字节。说明：中文字符按两个字节计算，英文字符按照字符占宽动态计算',
    ERROR_MIN_BYTE_LENGTH: '长度不能小于{0}字节。说明：中文字符按两个字节计算，英文字符按照字符占宽动态计算',
    ERROR_MAX_BYTE_LENGTH: '长度不能大于{0}字节。说明：中文字符按两个字节计算，英文字符按照字符占宽动态计算',
    ERROR_MIN_TEXT_BYTE_LENGTH: '文本长度不能小于{0}字节',
    ERROR_MAX_TEXT_BYTE_LENGTH: '文本长度不能大于{0}字节',
    ERROR_PRECISION: '请保留{0}位小数',
    ERROR_MAX_PRECISION: '最多保留{0}位小数',
    ERROR_TAG_COUNT: '必须包含{1}个{0}',
    ERROR_TAG_MIN_COUNT: '至少要有{1}个{0}',
    ERROR_TAG_MAX_COUNT: '不能超过{1}个{0}',
    ERROR_TAG_FIX_LENGTH: '长度必须等于{0}字符',
    ERROR_TAG_MIN_LENGTH: '{0}长度不能小于{0}字符',
    ERROR_TAG_MAX_LENGTH: '{0}长度不能大于{0}字符',
    ERROR_TAG_MIN_TEXT_BYTE_LENGTH: '{0}内文字长度不能小于{1}字节',
    ERROR_TAG_MAX_TEXT_BYTE_LENGTH: '{0}内文字长度不能大于{1}字节',
    ERROR_MAX_LINE_COUNT: '行数不能大于{0}',
    ERROR_BACKEND: '{0}',
    ERROR_TELEPHONE: '格式错误，请按区号-电话号码格式填写',
    ERROR_MOBILE_PHONE: '格式错误，请填写手机号码',
    ERROR_BUSINESS_TELEPHONE: '格式错误，请输入正确的400/800号码、座机号码',
    ERROR_PHONE: '格式错误，请输入正确的400/800号码、座机或手机号码',
    ERROR_LINK: '链接地址为无效地址',
    ERROR_LINK_MAX_LENGTH: '不能超过1024个字符',
    ERROR_LINK_DOWNLOAD: '不允许填写“.apk”的下载链接',
    ERROR_IMAGE_URL: '图片格式必须为jpg, gif或png',
    ERROR_FLV_URL: '图片格式必须为flv',
    ERROR_FLASH_URL: '图片格式必须为swf或flv',
    ERROR_VIDEO_URL: '图片格式必须为mp4或ts',
    ERROR_AUDIO_URL: '格式必须为音频文件格式',
    ERROR_NOT_LINK: '不能填写http或https链接地址',
    ERROR_APP_SCHEME: 'app scheme 填写错误',
    ERROR_EXCLUDES_LINK_TAG_PATTERN: '不能包含链接标签'
};

type NilOrString = null | undefined | string;
type NilOrSimple = null | undefined | number | string;

export type RulesFun<T extends RulesKey> = Record<RulesKey, (inputValue: any, param: RulesItem[T]) => ValidateResult>;

const required: RulesFunType<'required'> = (inputValue, isRequired) => {
    // 非必填，直接返回true
    if (!isRequired) {
        return true;
    }
    if (isAllTypeEmpty(inputValue)) {
        return errorTextMap.ERROR_EMPTY;
    }
    return true;
};
const regex: RulesFunType<'regex'> = (inputValue, params) => {
    if (isAllTypeEmpty(inputValue) || isAllTypeEmpty(params)) {
        return true;
    }
    if (!params || !params.pattern) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    let pattern = params.pattern;
    const modifiers = params.modifiers;
    const errorText = params.errorText;
    if (pattern && startsWith(pattern, '^http://')) {
        pattern = pattern.replace('^http://', '^https?://');
    }
    const reg = modifiers ? new RegExp(pattern, modifiers) : new RegExp(pattern);
    if (pattern && inputValue && !reg.test(inputValue)) {
        return errorText ? errorText : errorTextMap.ERROR_REGEX;
    }
    return true;
};
const jsRule: RulesFunType<'jsRule'> = (inputValue, ruleStr, parentValue) => {
    let errMsg = '';
    if (!ruleStr) {
        return true;
    }
    try {
        errMsg = new Function('return ' + ruleStr)().call(null, inputValue, parentValue);
    } catch (e) {
        errMsg = 'jsRule错误';
    }
    if (errMsg) {
        return errMsg;
    }
    return true;
};
const inEnum: RulesFunType<'inEnum'> = (inputValue, datasource) => {
    if (isAllTypeEmpty(inputValue) || !datasource || !isArray(datasource)) {
        return true;
    }
    if (isUndefined(datasource) || !includes(datasource, inputValue)) {
        return errorTextMap.ERROR_ENUM;
    }
    return true;
};
const email: RulesFunType<'email'> = (inputValue, flag) => {
    if (!flag || isAllTypeEmpty(inputValue)) {
        return true;
    }
    const email = inputValue as string;
    const len = email.length;
    if (len > 200) {
        return '邮箱超长';
    } else if (!/^.+@.+$/.test(email)) {
        return '邮箱格式错误';
    }
    return true;
};
const maxLineCount: RulesFunType<'maxLineCount'> = (inputValue, maxCount) => {
    if (isAllTypeEmpty(inputValue)) {
        return true;
    }
    if (isUndefined(maxCount)) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    const matches = trim(inputValue as string).match(/\r?\n/g) || [];
    const count = matches.length + 1;
    if (count > maxCount) {
        return stringFormat(errorTextMap.ERROR_MAX_LINE_COUNT, `${maxCount}`);
    }
    return true;
};
const excludesLinkTag: RulesFunType<'excludesLinkTag'> = (html, flag) => {
    if (!flag || isAllTypeEmpty(html)) {
        return true;
    }
    const linkStartTagReg = /<a\s[^>]*href=['"]([^'"]+)['"](\s|>)/;
    const linkEndTagReg = /<\/a>/;
    if (linkStartTagReg.test(html) && linkEndTagReg.test(html)) {
        return errorTextMap.ERROR_EXCLUDES_LINK_TAG_PATTERN;
    }
    return true;
};
const tagList: RulesFunType<'tagList'> = (html, list) => {
    if (isAllTypeEmpty(html)) {
        return true;
    }
    const nameMap: TagName = {
        em: '飘红词',
        a: '链接',
        br: '换行'
    };
    // '<' + tag + '>([^<]*)<\/' + tag + '>'
    for (const key in list) {
        if (!list.hasOwnProperty(key)) {
            continue;
        }
        const tag = key.toLowerCase();
        let code = html as string;
        const options = list[tag as keyof TagName] as RulesItem;
        const selfReg = new RegExp('<' + tag + '(\\/>|\\s[^>]*\\/>)', 'ig');
        const pairReg = new RegExp('<' + tag + '(>|\\s[^>]*>)(([^<]|<(?!\\/' + tag + '>))*)<\\/' + tag + '>', 'ig');
        const textArr = [];
        const htmlArr = [];
        const outerHtmlArr = [];
        let count = 0;
        let match;
        while ((match = selfReg.exec(code))) {
            count++;
        }
        code = code.replace(selfReg, '');
        while ((match = pairReg.exec(code))) {
            outerHtmlArr.push(match[0]);
            htmlArr.push(match[2]);
            textArr.push(getText(match[2]));
            count++;
        }
        if (tag === 'a') {
            for (let i = 0; i < outerHtmlArr.length; i++) {
                const ms = /<a\s[^>]*href=['"]([^'"]+)['"](\s|>)/.exec(outerHtmlArr[i]) || [];
                if (ms.length >= 2) {
                    if (true !== validateUrl(ms[1])) {
                        return stringFormat('链接“{0}”的url{1}', textArr[i], validateUrl(ms[1]) as string);
                    }
                }
            }
        }
        const tagName = nameMap[tag as keyof TagName];
        for (const rule in options) {
            if (!options.hasOwnProperty(rule)) {
                continue;
            }
            const value = options[rule as RulesKey] as number;
            switch (rule) {
                case 'count':
                    if (count !== value) {
                        return stringFormat(errorTextMap.ERROR_TAG_COUNT, tagName || tag, value);
                    }
                    break;
                case 'minCount':
                    if (count < value) {
                        return stringFormat(errorTextMap.ERROR_TAG_MIN_COUNT, tagName || tag, value);
                    }
                    break;
                case 'maxCount':
                    if (count > value) {
                        return stringFormat(errorTextMap.ERROR_TAG_MAX_COUNT, tagName || tag, value);
                    }
                    break;
                case 'minTextByteLength':
                    for (let i = 0; i < textArr.length; i++) {
                        if (getTextByteLength(textArr[i]) < value) {
                            return stringFormat(errorTextMap.ERROR_TAG_MIN_TEXT_BYTE_LENGTH, tagName || tag, value);
                        }
                    }
                    break;
                case 'maxTextByteLength':
                    for (let i = 0; i < textArr.length; i++) {
                        if (getTextByteLength(textArr[i]) > value) {
                            return stringFormat(errorTextMap.ERROR_TAG_MAX_TEXT_BYTE_LENGTH, tagName || tag, value);
                        }
                    }
                    break;
                case 'minByteLength':
                    for (let i = 0; i < textArr.length; i++) {
                        if (getByteLength(textArr[i]) < value) {
                            return stringFormat(errorTextMap.ERROR_TAG_MIN_TEXT_BYTE_LENGTH, tagName || tag, value);
                        }
                    }
                    break;
                case 'maxByteLength':
                    for (let i = 0; i < textArr.length; i++) {
                        if (getByteLength(textArr[i]) > value) {
                            return stringFormat(errorTextMap.ERROR_TAG_MAX_TEXT_BYTE_LENGTH, tagName || tag, value);
                        }
                    }
                    break;
                case 'minLength':
                    for (let i = 0; i < textArr.length; i++) {
                        if (getLength(textArr[i]) < value) {
                            return stringFormat(errorTextMap.ERROR_TAG_MIN_LENGTH, tagName || tag, value);
                        }
                    }
                    break;
                case 'maxLength':
                    for (let i = 0; i < textArr.length; i++) {
                        if (getLength(textArr[i]) > value) {
                            return stringFormat(errorTextMap.ERROR_TAG_MAX_LENGTH, tagName || tag, value);
                        }
                    }
                    break;
                case 'length':
                    for (let i = 0; i < textArr.length; i++) {
                        if (getLength(textArr[i]) === value) {
                            return stringFormat(errorTextMap.ERROR_TAG_FIX_LENGTH, tagName || tag, value);
                        }
                    }
                    break;
            }
        }
    }
    return true;
};
const telephone: RulesFunType<'telephone'> = (inputValue, flag) => {
    if (!flag || isAllTypeEmpty(inputValue)) {
        return true;
    }
    const text = (inputValue as string).replace(/-/g, '').replace(/\s/g, '');
    const rusult = /^(0\d{2,3})?(\d{7,8})(\d{3,5})?$/.test(text);
    if (!rusult) {
        return errorTextMap.ERROR_TELEPHONE;
    }
    return true;
};
const fax: RulesFunType<'fax'> = (inputValue, flag) => {
    if (!flag || isAllTypeEmpty(inputValue)) {
        return true;
    }
    const text = (inputValue as string).replace(/-/g, '').replace(/\s/g, '');
    const rusult = /^(0\d{2,3})?(\d{7,8})(\d{3,5})?$/.test(text);
    if (!rusult) {
        return errorTextMap.ERROR_TELEPHONE;
    }
    return true;
};
const mobilephone: RulesFunType<'mobilephone'> = (inputValue, flag) => {
    if (!flag || isAllTypeEmpty(inputValue)) {
        return true;
    }
    const text = (inputValue as string).replace(/-/g, '').replace(/\s/g, '');
    const rusult = /^1[3,4,5,6,7,8,9]{1}[0-9]{9}$/.test(text);
    if (!rusult) {
        return errorTextMap.ERROR_MOBILE_PHONE;
    }
    return true;
};
const businessPhone: RulesFunType<'businessPhone'> = (inputValue, flag) => {
    if (!flag || isAllTypeEmpty(inputValue)) {
        return true;
    }
    const text = (inputValue as string).replace(/-/g, '').replace(/\s/g, '');
    const phone1010 = /^([+]86)?1010[0-9]{4}$/.test(text);
    const phone800 = /^([+]86)?800[0-9]{7,9}$/.test(text);
    const phone400 = /^([+]86)?400[0-9]{7,9}$/.test(text);
    const shortPhone = /^([+]86)?(0\d{2,3})?[189]\d{4,5}$/.test(text);
    const hongkongPhone = /^(852)\d{8}$/.test(text);
    const macaoPhone = /^(853)\d{8}$/.test(text);
    const phone = telephone(text, true) === true;
    if (!(phone1010 || phone800 || phone400 || shortPhone || phone || hongkongPhone || macaoPhone)) {
        return errorTextMap.ERROR_BUSINESS_TELEPHONE;
    }
    return true;
};
const phone: RulesFunType<'phone'> = (inputValue, flag) => {
    if (!flag || isAllTypeEmpty(inputValue)) {
        return true;
    }
    const text = inputValue as string;
    const phoneResult = telephone(text, true) === true;
    const businessPhoneResult = businessPhone(text, true) === true;
    const mobilephoneResult = mobilephone(text, true) === true;
    const faxResult = fax(text, true) === true;
    const telephoneResult = telephone(text, true) === true;
    if (!(phoneResult || businessPhoneResult || mobilephoneResult || faxResult || telephoneResult)) {
        return errorTextMap.ERROR_PHONE;
    }
    return true;
};
const count: RulesFunType<'count'> = (inputValue, length) => {
    if (isAllTypeEmpty(inputValue)) {
        return true;
    }
    if (isUndefined(length)) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    if (inputValue.length !== length) {
        return stringFormat(errorTextMap.ERROR_COUNT, length);
    }
    return true;
};
const minCount: RulesFunType<'minCount'> = (inputValue, length) => {
    if (isAllTypeEmpty(inputValue)) {
        return true;
    }
    if (isUndefined(length)) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    if (inputValue.length < length) {
        return stringFormat(errorTextMap.ERROR_MIN_COUNT, length);
    }
    return true;
};
const maxCount: RulesFunType<'maxCount'> = (inputValue, length) => {
    if (isAllTypeEmpty(inputValue)) {
        return true;
    }
    if (isUndefined(length)) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    if (inputValue.length > length) {
        return stringFormat(errorTextMap.ERROR_MAX_COUNT, length);
    }
    return true;
};
const multipleCount: RulesFunType<'multipleCount'> = (inputValue, lengthFactor) => {
    if (isAllTypeEmpty(inputValue) || isUndefined(lengthFactor)) {
        return true;
    }
    if (inputValue.length % lengthFactor !== 0) {
        return stringFormat(errorTextMap.ERROR_MULTIPLE_COUNT, lengthFactor);
    }
    return true;
};
const fixByteLength: RulesFunType<'fixByteLength'> = (inputValue, length) => {
    if (isAllTypeEmpty(inputValue)) {
        return true;
    }
    if (isUndefined(length)) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    const text = inputValue as string;
    if (getByteLength(text) !== length) {
        return stringFormat(errorTextMap.ERROR_FIX_BYTE_LENGTH, length);
    }
    return true;
};
const minByteLength: RulesFunType<'minByteLength'> = (inputValue, length) => {
    if (isAllTypeEmpty(inputValue)) {
        return true;
    }
    if (isUndefined(length)) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    if (getByteLength(inputValue as string) < length) {
        return stringFormat(errorTextMap.ERROR_MIN_BYTE_LENGTH, length);
    }
    return true;
};
const maxByteLength: RulesFunType<'maxByteLength'> = (inputValue, length) => {
    if (isAllTypeEmpty(inputValue)) {
        return true;
    }
    if (isUndefined(length)) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    if (getByteLength(inputValue as string) > length) {
        return stringFormat(errorTextMap.ERROR_MAX_BYTE_LENGTH, length);
    }
    return true;
};
const minTextByteLength: RulesFunType<'minTextByteLength'> = (inputValue, length) => {
    if (isAllTypeEmpty(inputValue)) {
        return true;
    }
    if (isUndefined(length)) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    const html = inputValue as string;
    if (getTextByteLength(html) < length) {
        return stringFormat(errorTextMap.ERROR_MIN_TEXT_BYTE_LENGTH, length);
    }
    return true;
};
const maxTextByteLength: RulesFunType<'maxTextByteLength'> = (inputValue, length) => {
    if (isAllTypeEmpty(inputValue)) {
        return true;
    }
    if (isUndefined(length)) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    const html = inputValue as string;
    if (html && getTextByteLength(html) > length) {
        return stringFormat(errorTextMap.ERROR_MAX_TEXT_BYTE_LENGTH, length);
    }
    return true;
};
const fixLength: RulesFunType<'fixLength'> = (inputValue, length) => {
    if (isAllTypeEmpty(inputValue)) {
        return true;
    }
    if (isUndefined(length)) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    const text = inputValue as string;
    if (text.length !== length) {
        return stringFormat(errorTextMap.ERROR_FIX_LENGTH, length);
    }
    return true;
};
const minLength: RulesFunType<'minLength'> = (inputValue, length) => {
    if (isAllTypeEmpty(inputValue)) {
        return true;
    }
    if (isUndefined(length)) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    const text = inputValue as string;
    if (text.length < length) {
        return stringFormat(errorTextMap.ERROR_MIN_LENGTH, length);
    }
    return true;
};
const maxLength: RulesFunType<'maxLength'> = (inputValue, length) => {
    if (isAllTypeEmpty(inputValue)) {
        return true;
    }
    if (isUndefined(length)) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    const text = inputValue as string;
    if (text.length > length) {
        return stringFormat(errorTextMap.ERROR_MAX_LENGTH, length);
    }
    return true;
};
const date: RulesFunType<'date'> = (inputValue, dateParams) => {
    if (isAllTypeEmpty(inputValue) || isAllTypeEmpty(dateParams)) {
        return true;
    }
    const {format = 'YYYY-MM-DD', gt, lt} = dateParams || {};
    const text = inputValue as string;
    const inputDate = dayjs(text, format);
    const isValid = inputDate.isValid();
    if (!isValid) {
        return errorTextMap.ERROR_DATE;
    }
    if (gt && inputDate.isBefore(dayjs(gt, format))) {
        return stringFormat(errorTextMap.ERROR_GT_DATE, gt);
    }
    if (lt && inputDate.isAfter(dayjs(lt, format))) {
        return stringFormat(errorTextMap.ERROR_GT_DATE, lt);
    }
    return true;
};
const link: RulesFunType<'link'> = (inputValue, flag) => {
    if (!flag || isAllTypeEmpty(inputValue)) {
        return true;
    }
    const text = inputValue as string;
    if (true !== validateUrl(text)) {
        return validateUrl(text) as string;
    } else if (apkDownloadUrlReg.test(text)) {
        return errorTextMap.ERROR_LINK_DOWNLOAD;
    }
    return true;
};
function mediaUrl(inputValue: NilOrString, urlRegex: RegExp, errorText: string): ValidateResult {
    if (isAllTypeEmpty(inputValue)) {
        return true;
    }
    const text = inputValue as string;
    // 去除参数部分
    const extUrl = text.replace(/(\?.*)$/g, '');
    if (true !== validateUrl(text)) {
        return validateUrl(text) as string;
    } else if (!urlRegex.test(extUrl)) {
        return errorText;
    }
    return true;
}
function multipleMediaUrl(inputValue: string[], urlRegex: RegExp, errorText: string) {
    const results = map(inputValue, (url, index) => {
        let result = mediaUrl(url, urlRegex, errorText);
        if (result !== true) {
            result = `第${index + 1}张图片: ${result}`;
        }
        return result;
    });
    return filter(results, (it) => it !== true).join('；');
}
const imageUrl: RulesFunType<'imageUrl'> = (inputValue, flag) => {
    return (
        !flag ||
        (isArray(inputValue)
            ? multipleMediaUrl(inputValue, imgUrlReg, errorTextMap.ERROR_IMAGE_URL)
            : mediaUrl(inputValue, imgUrlReg, errorTextMap.ERROR_IMAGE_URL))
    );
};
const flvUrl: RulesFunType<'flvUrl'> = (inputValue, flag) => {
    return !flag || mediaUrl(inputValue, flvUrlReg, errorTextMap.ERROR_FLV_URL);
};
const flashUrl: RulesFunType<'flashUrl'> = (inputValue, flag) => {
    return !flag || mediaUrl(inputValue, flashUrlReg, errorTextMap.ERROR_FLASH_URL);
};
const videoUrl: RulesFunType<'videoUrl'> = (inputValue, flag) => {
    return !flag || mediaUrl(inputValue, videoUrlReg, errorTextMap.ERROR_VIDEO_URL);
};
const audioUrl: RulesFunType<'audioUrl'> = (inputValue, flag) => {
    return !flag || mediaUrl(inputValue, audioUrlReg, errorTextMap.ERROR_AUDIO_URL);
};
function isBaseNumber(inputValue: NilOrSimple): ValidateResult | undefined {
    if (isAllTypeEmpty(inputValue)) {
        return true;
    }
    if (/^[0-9]+(\.[0-9]+)?$/.test(String(inputValue)) === false) {
        return errorTextMap.ERROR_NUMBER;
    }
}
const int: RulesFunType<'int'> = (inputValue, flag) => {
    const checkBase = !flag || isBaseNumber(inputValue);
    if (!isUndefined(checkBase)) {
        return checkBase;
    }
    const text = String(inputValue);
    if (includes(text, '.')) {
        return errorTextMap.ERROR_INT;
    }
    return true;
};
const number: RulesFunType<'number'> = (inputValue, flag) => {
    const checkBase = !flag || isBaseNumber(inputValue);
    if (!isUndefined(checkBase)) {
        return checkBase;
    }
    return true;
};
const min: RulesFunType<'min'> = (inputValue, compare) => {
    if (isAllTypeEmpty(compare)) {
        return true;
    }
    const checkBase = isBaseNumber(inputValue);
    if (!isUndefined(checkBase)) {
        return checkBase;
    }
    const compareValue = compare;
    if (isUndefined(compareValue)) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    const inputNumber = Number(inputValue);
    if (inputNumber < compareValue) {
        return stringFormat(errorTextMap.ERROR_MIN, compareValue);
    }
    return true;
};
const max: RulesFunType<'max'> = (inputValue, compare) => {
    if (isAllTypeEmpty(compare)) {
        return true;
    }
    const checkBase = isBaseNumber(inputValue);
    if (!isUndefined(checkBase)) {
        return checkBase;
    }
    const compareValue = compare;
    if (isUndefined(compareValue)) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    const inputNumber = Number(inputValue);
    if (inputNumber > compareValue) {
        return stringFormat(errorTextMap.ERROR_MAX, compareValue);
    }
    return true;
};
const gt: RulesFunType<'gt'> = (inputValue, compareValue) => {
    const checkBase = isBaseNumber(inputValue);
    if (!isUndefined(checkBase)) {
        return checkBase;
    }
    if (isUndefined(compareValue)) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    const inputNumber = Number(inputValue);
    if (inputNumber <= compareValue) {
        return stringFormat(errorTextMap.ERROR_GT, compareValue);
    }
    return true;
};
const lt: RulesFunType<'lt'> = (inputValue, compareValue) => {
    const checkBase = isBaseNumber(inputValue);
    if (!isUndefined(checkBase)) {
        return checkBase;
    }
    if (isUndefined(compareValue)) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    const inputNumber = Number(inputValue);
    if (inputNumber >= compareValue) {
        return stringFormat(errorTextMap.ERROR_LT, compareValue);
    }
    return true;
};
const precision: RulesFunType<'precision'> = (inputValue, length) => {
    const checkBase = isBaseNumber(inputValue);
    if (!isUndefined(checkBase)) {
        return checkBase;
    }
    if (isUndefined(length)) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    const NumberParts = String(inputValue).split('.');
    if (NumberParts.length !== 2 || !NumberParts[1] || NumberParts[1].length !== length) {
        return stringFormat(errorTextMap.ERROR_PRECISION, length);
    }
    return true;
};
const maxPrecision: RulesFunType<'maxPrecision'> = (inputValue, length) => {
    const checkBase = isBaseNumber(inputValue);
    if (!isUndefined(checkBase)) {
        return checkBase;
    }
    if (isUndefined(length)) {
        return errorTextMap.ERROR_NO_PATTERN;
    }
    const NumberParts = String(inputValue).split('.');
    if (NumberParts[1] && NumberParts[1].length > length) {
        return stringFormat(errorTextMap.ERROR_MAX_PRECISION, length);
    }
    return true;
};

const notLink: RulesFunType<'notLink'> = (inputValue, flag) => {
    if (!flag || isAllTypeEmpty(inputValue)) {
        return true;
    }
    const text = inputValue as string;
    if (/^(https?):\/{2}?/i.test(text) === true) {
        return errorTextMap.ERROR_NOT_LINK;
    }
    return true;
};

const appScheme: RulesFunType<'notLink'> = (inputValue, flag) => {
    if (!flag || isAllTypeEmpty(inputValue)) {
        return true;
    }
    const text = inputValue as string;
    if (validateAppScheme(text) === false) {
        return errorTextMap.ERROR_APP_SCHEME;
    }
    return true;
};

export const rules = {
    required,
    regex,
    jsRule,
    inEnum,
    email,
    maxLineCount,
    tagList,
    excludesLinkTag,
    telephone,
    fax,
    mobilephone,
    businessPhone,
    phone,
    count,
    minCount,
    maxCount,
    multipleCount,
    fixByteLength,
    minByteLength,
    maxByteLength,
    minTextByteLength,
    maxTextByteLength,
    fixLength,
    minLength,
    maxLength,
    date,
    link,
    imageUrl,
    flvUrl,
    flashUrl,
    videoUrl,
    audioUrl,
    int,
    number,
    min,
    max,
    gt,
    lt,
    precision,
    maxPrecision,
    notLink,
    appScheme
} as RulesFun<RulesKey>;
